package mundo;

import interfaz.*;
import java.util.*;
import objetos.*;
import objetos.personajes.*;
import java.awt.*;
import javax.swing.*;
import java.awt.image.*;
import persistencia.*;
import XML.*;

/** @author Yanire, Miguel (el mejor)  CARLOS**/
public class GestorMapas {

    private int nivel = 0;
    private Mapa mapa; //mapa actual
    private Bando bandos[];
    private Dibujador dibujador;
    private int turno; //0: patriota / 1:realista
    public int turnoJugador;
    private String cadenaAtaque = new String();
    private InteligenciaArtificial ai;
    private Personaje[] personajes; //poner en constructor
    private ArrayList<Personaje> seleccionado;
    private int bonoW;
    private int bonoL;
    private int numNiveles;
    private int[] coordenadasInicio;
    private int[] coordenadasFin;
    private ControladorGestor controlador;
    private int minDamage;

    public int getBandoPC() {
        return turnoJugador == 0 ? 1 : 0;
    }

    public int getBandoRival(int otroBando) {
        return otroBando == 0 ? 1 : 0;
    }

//    <editor-fold defaultstate="collapsed" desc="Constructor">
//    Gestor se banca la creacion del dibujador, bandos, batallones actuales, turno y turnoJugador
    public GestorMapas() {
        MapaXML mapaxml = XML_DeParser.deserializacionMapa(nivel);
        InfoGeneral infoxml = XML_DeParser.deserializacionInfoGeneral();
        dibujador = new Dibujador(infoxml.A, infoxml.B);
        minDamage = infoxml.minDamage;
        seleccionado = new ArrayList<Personaje>();
        //    Construye el arreglo de 2 bandos
        bandos = new Bando[2];
        bonoW = infoxml.bonoW;
        bonoL = infoxml.bonoL;
        numNiveles = infoxml.numNiveles;
        Personaje.DireccionArtificial = 1;
        //bandos[0] = XML_DeParser.generarBando(XML_DeParser.deserializacionBatallon(0, nivel), 0);
        //bandos[1] = XML_DeParser.generarBando(XML_DeParser.deserializacionBatallon(1, nivel), 1);

        bandos[0] = new Bando(0);
        //  bandos[0].construyeBandoPatriota(nivel);

        bandos[1] = new Bando(1);
        //  bandos[1].construyeBandoRealista(nivel);



        //    Construye el mapa actual
        mapa = XML_DeParser.generarMapa(mapaxml, bandos[0], bandos[1]);
        //  Crea el arreglo de los personajes donde comenzara el turno
        personajes = new Personaje[2];

        personajes[0] = bandos[0].getPersonajePrincipal();
        personajes[1] = bandos[1].getPersonajePrincipal();

        turno = turnoJugador;

        bandos[0].setDinero(infoxml.dinero0);
        bandos[1].setDinero(infoxml.dinero1);

        ai = new InteligenciaArtificial(this);
        controlador = new ControladorGestor(this);
    }

    public void MostrarMapa(Graphics2D graph2D, ImageObserver observer) {
        dibujador.MostrarMapa(graph2D, mapa, personajes[turno].getX(), personajes[turno].getY(), observer);

    }

    public boolean deserializar(String nombreArch) {
        Serializacion archivo = new Serializacion();

        Partida partida = archivo.Deserializacion(nombreArch);

        nivel = partida.getGestor().getNivel();
        //    Construye el arreglo de 2 bandos
        bandos = partida.getGestor().getBandos();

        ArrayList<Aldea> aldeas = partida.getAldeas();

        //    Construye el mapa actual
        mapa = new Mapa(nivel, bandos, aldeas);
        //  Crea el arreglo de los batallones donde comenzara el turno
        personajes = partida.getGestor().getPersonajes();

        turno = partida.getGestor().getTurnoJugador();

        return !(partida == null);
    }
//    </editor-fold>

//    <editor-fold defaultstate="collapsed" desc="Acciones">
    /**
     * Método que rota los batallones del bando objetivo para poder manejar el
     * siguiente batallón durante el juego.
     */
    public int cambiar(int bando) {
        if (!bandos[bando].getPersonajes().isEmpty()) {
            personajes[bando] = bandos[bando].getSiguientePersonaje(personajes[bando]);
        }
        return 1;
    }

    public void mover(int dir) {
        //    Toma las coordenadas del batallon actual
        int x = personajes[turno].getX();
        int y = personajes[turno].getY();

        //    Toma coordenadas objetivo
        switch (dir) {
            case 9:
                x--;
                break;
            case 3:
                x++;
                break;
            case 12:
                y--;
                break;
            case 6:
                y++;
                break;
        }

        boolean movValido = false;

        //    Evalua si la celda objetivo esta vacia
        movValido = mapa.esCeldaDeMapa(x, y) && mapa.getCelda(x, y).getTipo().equals("Vacio");

        if (movValido) {
            mapa.modificaCelda(x, y, personajes[turno]);
        }

        for(int i=0; i<seleccionado.size(); i++){
            if(seleccionado.get(i)!=personajes[turno]) {
                x = seleccionado.get(i).getX();
                y = seleccionado.get(i).getY();

                //    Toma coordenadas objetivo
                switch (dir) {
                    case 9:
                        x--;
                        break;
                    case 3:
                        x++;
                        break;
                    case 12:
                        y--;
                        break;
                    case 6:
                        y++;
                        break;
                }

                movValido = false;

                //    Evalua si la celda objetivo esta vacia
                movValido = mapa.esCeldaDeMapa(x, y) && mapa.getCelda(x, y).getTipo().equals("Vacio");

                if (movValido) {
                    mapa.modificaCelda(x, y, seleccionado.get(i));
                }
            }
        }

    }

    /**
     * Método que rota los personajes del bando objetivo para poder manejar el
     * siguiente personaje de la lista durante el juego.
     */
    public void cambiar() {
        if (!bandos[turno].getPersonajes().isEmpty()) {
            personajes[turno] = bandos[turno].getSiguientePersonaje(personajes[turno]);
        }
    }

    public void comprarPersonajes(int dir, LaVentana ventana) {

        //    Toma las coordenadas del batallon actual
        int x = personajes[turno].getX();
        int y = personajes[turno].getY();
        //    Toma coordenadas objetivo
        switch (dir) {
            case 9:
                x--;
                break;
            case 3:
                x++;
                break;
            case 12:
                y--;
                break;
            case 6:
                y++;
                break;
        }
        //    Evalua si la celda objetivo esta vacia
        //    Si sale del bucle es porque el movimiento es valido; se ejecuta dicho movimiento
        //    Los metodos de compra regresaran un booleano que representa el estado de la compra

        Aldea aldea = (Aldea) (mapa.getCelda(x, y).getObjeto());

        if (!mapa.noRodearAldea(aldea)) {

            Personaje personajeNuevo = (aldea.comprarPersonaje(ventana, bandos[turno]));
            if (personajeNuevo != null) {
                HiloRetardo retardo = new HiloRetardo(bandos[turnoJugador], personajeNuevo, controlador, aldea);
                retardo.start();
                System.out.println("RETARDO JUG");
                //bando.agregarPersonaje(personaje);
                //controlador.asignarCelda(aldea, personajeNuevo);
            }
        } else {
            JOptionPane.showMessageDialog(ventana, "No hay espacio para colocar al personaje", "Compra Anulada", JOptionPane.ERROR_MESSAGE);
        }

    }

    public void comprarArmas(int dir, LaVentana ventana) {

        //    Toma las coordenadas del batallon actual
        int x = personajes[turno].getX();
        int y = personajes[turno].getY();

        //    Toma coordenadas objetivo
        switch (dir) {
            case 9:
                x--;
                break;
            case 3:
                x++;
                break;
            case 12:
                y--;
                break;
            case 6:
                y++;
                break;
        }
        //    Evalua si la celda objetivo esta vacia
        //    Si sale del bucle es porque el movimiento es valido; se ejecuta dicho movimiento
        //    Los metodos de compra regresaran un booleano que representa el estado de la compra

        Aldea aldea = (Aldea) (mapa.getCelda(x, y).getObjeto());
        aldea.comprarArma(ventana, personajes[turno], bandos[turno]);

    }

    public boolean atacar(int dir){
        if(seleccionado.isEmpty())
            return atacar(dir, personajes[turno]);
        else {
            boolean esfinJuego = true;
            for(int i = 0; i< seleccionado.size(); i++){
                System.out.println("\n\nATACACLICK\n\n");
                esfinJuego = esfinJuego && atacar(dir, seleccionado.get(i));
                System.out.println("----Cadena:" + cadenaAtaque + "Personaje:" + seleccionado.get(i));
            }
            return esfinJuego;
        }
    }

    /**
     * Devuelve true si se termina el juego debido a este ataque y false en caso
     * se cambie de turno y el juego continúe.
     */
    public boolean atacar(int dir, Personaje personaje) {
        //Toma las coordenadas del batallon actual
        int x = personaje.getX();
        int y = personaje.getY();
        int ataqueTurno = personaje.ataqueTotal();//incluido todas las armas

        if((dir = mapa.hayEnemigoAdyacente(personaje)) !=0){
            Personaje personajeRival = null;
            boolean movValido = false;
            boolean finJuego = false;
            switch (dir) {
                case 9:
                    x--;
                    break;
                case 3:
                    x++;
                    break;
                case 12:
                    y--;
                    break;
                case 6:
                    y++;
                    break;
            }
            //Evalua si la celda objetivo es un batallon rival
            movValido = mapa.esCeldaDeMapa(x, y) && mapa.getCelda(x, y).getObjeto() instanceof Personaje;
            if (movValido) {
                personajeRival = (Personaje) (mapa.getCelda(x, y).getObjeto());
                movValido = (personajeRival.getBando() == getRival());
                if(!movValido) return false;
            }

            int defensaRival = personajeRival.defensaTotal();//defensa con todas sus armas

            cadenaAtaque = "¡Atacaste!";

            //Posteriormente acá se añadirá un gráfico que represente la vida
            cadenaAtaque += "Vida del atacado: ";

            int damage;
            //se ejecuta dicho ataque
            if (ataqueTurno <= defensaRival + minDamage) //si su def es mayor le bajo el daño minimo
            {
                damage = minDamage;
            } else {
                damage = ataqueTurno - defensaRival;
            }

            personajeRival.disminuyeVida(damage);

            if (personajeRival.getVidaActual() <= 0) {
                cadenaAtaque += "0";
                if (personajeRival.getBando() != personaje.getBando()) {
                    cambiar(getRival());
                }

               // mapa.getCelda(personajeRival.getX(), personajeRival.getY()).setObjeto(null);

                //ahora debemo sacarlo del arreglo de personajes
                return(bandos[getRival()].eliminarPersonajePostCombate(personajeRival));

            } else {
                cadenaAtaque += personajeRival.getVidaActual();
            }
        }
        return false;
    }

//    </editor-fold>
    public boolean atacarPC(Personaje personajePC, int dir) {

        int x = personajePC.getX();
        int y = personajePC.getY();
        int ataqueTurno = personajePC.ataqueTotal();//incluido todas las armas

        if ((dir = mapa.hayEnemigoAdyacente(personajePC)) != 0){
            System.out.println("DIRECCION"+dir);
            Personaje personajeRival = null;
            boolean movValido = false;
            boolean finJuego = false;
            switch (dir) {
                case 9:
                    x--;
                    break;
                case 3:
                    x++;
                    break;
                case 12:
                    y--;
                    break;
                case 6:
                    y++;
                    break;
            }

            //Evalua si la celda objetivo es un batallon rival
            movValido = mapa.esCeldaDeMapa(x, y) && mapa.getCelda(x, y).getObjeto() instanceof Personaje;

            if (movValido) {
                personajeRival = (Personaje) (mapa.getCelda(x, y).getObjeto());
                //System.out.println(dir + "" + x + "" + y);
                movValido = (personajeRival.getBando() == getBandoRival(personajePC.getBando()));
            }
              if (!movValido)  return false;

            int defensaRival = personajeRival.defensaTotal();//defensa con todas sus armas

            //cadenaAtaque = "¡Ataco la maquina!";
            //Posteriormente acá se añadirá un gráfico que represente la vida
            //cadenaAtaque += "Vida del atacado: ";

            int damage;
            //se ejecuta dicho ataque
            if (ataqueTurno <= defensaRival + minDamage) //si su def es mayor le bajo el daño minimo
            {
                damage = minDamage;

            } else {
                damage = ataqueTurno - defensaRival;
            }
            System.out.println("La maquina te bajo:" + damage);

            personajeRival.disminuyeVida(damage);

            if (personajeRival.getVidaActual() <= 0) {
                //cadenaAtaque += "0";
                if (personajeRival == personajes[getBandoRival(personajePC.getBando())]) {
                    cambiar(getBandoRival(personajePC.getBando()));
                    System.out.println("CAMBIO");
                } else{
                    for (int i=0; i<seleccionado.size(); i++)
                        if (seleccionado.get(i).equals(personajeRival)){
                            seleccionado.remove(i);
                            System.out.println("BBBBB: ");

                        }
                }

                mapa.getCelda(personajeRival.getX(), personajeRival.getY()).setObjeto(null);

                //ahora debemo sacarlo del arreglo de personajes
                System.out.println("PC"+personajePC.getBando());
                System.out.println("RIVAL"+getBandoRival(personajePC.getBando()));
                return (bandos[getBandoRival(personajePC.getBando())].eliminarPersonajePostCombate(personajeRival));
            } else {
                //cadenaAtaque += personajeRival.getVidaActual();
            }
        }
        return false;
    }

//    <editor-fold defaultstate="collapsed" desc="Curacion y Manutencion">
    public String actualizarManutencion() {
        String cadena = new String();

        cadena = "Variacion de dinero por manutencion: ";
        cadena += bandos[turnoJugador].getDinero();

        bandos[turnoJugador].manutencion();

        cadena += "-> ";
        cadena += bandos[turnoJugador].getDinero();

        return cadena;
    }

    public String actualizarCuracion() {
        String cadena = new String();

        cadena = "Variacion de dinero por curacion: ";
        cadena += bandos[turno].getDinero();

        bandos[turno].curacion();

        cadena += "-> ";
        cadena += bandos[turno].getDinero();

        return cadena;
    }


//    </editor-fold>
//    <editor-fold defaultstate="collapsed" desc="Get & Set">



    public ArrayList<Personaje> getSeleccionado() {
        return seleccionado;
    }

    public void setSeleccionado(ArrayList<Personaje> seleccionado) {
        this.seleccionado = seleccionado;
    }

    public int getTurno() {
        return turno;
    }

    public Personaje[] getPersonajes() {
        return personajes;
    }

    public int getNivel() {
        return nivel;
    }

    public Mapa getMapa() {
        return mapa;
    }

    /*
     *Metodo que devuelve el indice del bando rival
     *
     */
    public int getRival() {
        return (turnoJugador == 0) ? 1 : 0;
    }

    /*
     *Metodo que devuelve un entero desde 0 hasta n-1
     *
     */
    public static int generarAleatorio(int n) {
        Random generator = new Random();
        return generator.nextInt(n);
    }

    /*
     *Metodo que devuelve el bando que eligio el jugador
     *
     */
    public void getJugador(LaVentana ventana) {
        String[] opciones = {"Patriota", "Realista"};
        turnoJugador = -1;

        while (turnoJugador == -1) {
            turnoJugador = JOptionPane.showOptionDialog(ventana, "Elija un bando", "Bienvenido", 2, 1, null, opciones, opciones[0]);
        }

        turno = turnoJugador;
    }

    /**
     * @return the turnoJugador
     */
    public int getTurnoJugador() {
        return turnoJugador;
    }

    /**
     * @param turnoJugador the turnoJugador to set
     */
    public void setTurnoJugador(int turnoJugador) {
        this.turnoJugador = turnoJugador;
    }

    public Bando[] getBandos() {
        return bandos;
    }

    /**
     * Imprime la información referente a los bandos posterior a un ataque.
     */
    public String getAtacar() {
        return cadenaAtaque;
    }

    public Object getContentCelda(int x, int y) {
        return mapa.getCelda(x, y).getObjeto();
    }

    public void setAi(InteligenciaArtificial ai) {
        this.ai = ai;
    }

    public void setBandos(Bando[] bandos) {
        this.bandos = bandos;
    }

    public void setPersonajes(Personaje[] personajes) {
        this.personajes = personajes;
    }

    public void setCadenaAtaque(String cadenaAtaque) {
        this.cadenaAtaque = cadenaAtaque;
    }

    public void setDibujador(Dibujador dibujador) {
        this.dibujador = dibujador;
    }

    public void setMapa(Mapa mapa) {
        this.mapa = mapa;
    }

    public void setNivel(int nivel) {
        this.nivel = nivel;
    }

    public void setTurno(int turno) {
        this.turno = turno;
    }

    public ControladorGestor getControlador() {
        return controlador;
    }

    public Dibujador getDibujador() {
        return dibujador;
    }

    public int getGanador() {
        if (bandos[turno].getDinero() > 0) {
            return turno;
        }
        return turno == 1 ? 0 : 1;
    }

    public int[] getCoordenadasFin() {
        return coordenadasFin;
    }

    public void setCoordenadasFin(int x, int y) {
        int[] coordenadasFin = {x, y};
        this.coordenadasFin = coordenadasFin;
    }

    public int[] getCoordenadasInicio() {
        return coordenadasInicio;
    }

    public void setCoordenadasInicio(int x, int y) {
        int[] coordenadasInicio = {x, y};
        this.coordenadasInicio = coordenadasInicio;
    }

    /**
     * @return the ai
     */
    public InteligenciaArtificial getAi() {
        return ai;
    }

//    </editor-fold>
//    <editor-fold defaultstate="collapsed" desc="Metodos de accion sencillos">
    public void deseleccionar() {
        for (int i = 0; i < seleccionado.size(); i++) {
            seleccionado.get(i).setSeleccionado(false);
        }
        
        for (int i = seleccionado.size()-1; i >=0; i--) {
            seleccionado.remove(i);
        }

    }

    public void crearSeleccionado() {
        int[] celdaInicio = controlador.inMouseOutCelda(coordenadasInicio[0], coordenadasInicio[1]);
        int[] celdaFin = controlador.inMouseOutCelda(coordenadasFin[0], coordenadasFin[1]);

        if ((celdaFin[0] - celdaInicio[0]) > 0) {

            for (int i = 0; i < (celdaFin[0] - celdaInicio[0]); i++) {
                if ((celdaFin[1] - celdaInicio[1]) > 0) {
                    for (int j = 0; j < (celdaFin[1] - celdaInicio[1]); j++) {
                        if (mapa.esCeldaDeMapa(celdaInicio[0] + i, celdaInicio[1] + j)) {
                            if (mapa.getCelda(celdaInicio[0] + i, celdaInicio[1] + j).getObjeto() instanceof Personaje) {
                                if(((Personaje) (mapa.getCelda(celdaInicio[0] + i, celdaInicio[1] + j).getObjeto())).getBando()== turnoJugador){
                                    ((Personaje) (mapa.getCelda(celdaInicio[0] + i, celdaInicio[1] + j).getObjeto())).setSeleccionado(true);
                                    seleccionado.add(((Personaje) (mapa.getCelda(celdaInicio[0] + i, celdaInicio[1] + j).getObjeto())));
                                }
                            }
                        }

                    }
                }

                if ((celdaFin[1] - celdaInicio[1]) >= 0){
                    for (int j = 0; j < (celdaInicio[1] - celdaFin[1]); j++) {
                        if (mapa.esCeldaDeMapa(celdaInicio[0] + i, celdaInicio[1] - j)) {
                            if (mapa.getCelda(celdaInicio[0] + i, celdaInicio[1] - j).getObjeto() instanceof Personaje) {
                                if(((Personaje) (mapa.getCelda(celdaInicio[0] + i, celdaInicio[1] + j).getObjeto())).getBando()== turnoJugador){
                                    ((Personaje) (mapa.getCelda(celdaInicio[0] + i, celdaInicio[1] - j).getObjeto())).setSeleccionado(true);
                                    seleccionado.add(((Personaje) (mapa.getCelda(celdaInicio[0] + i, celdaInicio[1] - j).getObjeto())));
                                }
                            }
                        }
                    }
                }
            }
            return;
        }

        if ((celdaFin[0] - celdaInicio[0]) <= 0)  {
            for (int i = 0; i < (celdaInicio[0] - celdaFin[0]); i++) {
                if ((celdaInicio[1] - celdaFin[1]) > 0) {
                    for (int j = 0; j < (celdaInicio[1] - celdaFin[1]); j++) {
                        if (mapa.esCeldaDeMapa(celdaInicio[0] - i, celdaInicio[1] + j)) {
                            if (mapa.getCelda(celdaInicio[0] - i, celdaInicio[1] + j).getObjeto() instanceof Personaje) {
                                if(((Personaje) (mapa.getCelda(celdaInicio[0] + i, celdaInicio[1] + j).getObjeto())).getBando()== turnoJugador){
                                    ((Personaje) (mapa.getCelda(celdaInicio[0] - i, celdaInicio[1] + j).getObjeto())).setSeleccionado(true);
                                    seleccionado.add(((Personaje) (mapa.getCelda(celdaInicio[0] - i, celdaInicio[1] + j).getObjeto())));
                                }
                            }
                        }
                    }
                }
                if ((celdaInicio[1] - celdaFin[1]) <= 0){
                    for (int j = 0; j < (celdaFin[1] - celdaInicio[1]); j++) {
                        if (mapa.esCeldaDeMapa(celdaInicio[0] - i, celdaInicio[1] - j)) {
                            if (mapa.getCelda(celdaInicio[0] - i, celdaInicio[1] - j).getObjeto() instanceof Personaje) {
                                if(((Personaje) (mapa.getCelda(celdaInicio[0] + i, celdaInicio[1] + j).getObjeto())).getBando()== turnoJugador){
                                    ((Personaje) (mapa.getCelda(celdaInicio[0] - i, celdaInicio[1] - j).getObjeto())).setSeleccionado(true);
                                    seleccionado.add(((Personaje) (mapa.getCelda(celdaInicio[0] - i, celdaInicio[1] - j).getObjeto())));

                                }
                            }
                        }
                    }
                }
                return;
            }
        }

    }

    /**
     * Método que cambia de nivel y levanta el mapa siguiente.
     */
    public boolean cambiarNivel() {

        /*Cambia de nivel y levanta mapa sgte*/

        if (getGanador() == getTurnoJugador()) {
            nivel++;
            if (nivel < numNiveles) {
                MapaXML mapaxml = XML_DeParser.deserializacionMapa(nivel);

                InfoGeneral infoxml = XML_DeParser.deserializacionInfoGeneral();
                seleccionado = new ArrayList<Personaje>();

                bonoW = infoxml.bonoW;
                bonoL = infoxml.bonoL;
                numNiveles = infoxml.numNiveles;

                int dinero[] = {bandos[1].getDinero(), bandos[0].getDinero()};
                dinero[getTurnoJugador()] += bonoW;
                dinero[getTurnoJugador() == 0 ? 1 : 0] += bonoL;

                //    Construye el arreglo de 2 bandos
                bandos = new Bando[2];

                Personaje.DireccionArtificial = 1;
//                bandos[0] = XML_DeParser.generarBando(XML_DeParser.deserializacionBatallon(0, nivel), 0);
//                bandos[1] = XML_DeParser.generarBando(XML_DeParser.deserializacionBatallon(1, nivel), 1);

                bandos[0] = new Bando(0);
                bandos[0].construyeBandoPatriota(nivel);

                bandos[1] = new Bando(1);
                bandos[1].construyeBandoRealista(nivel);


                //    Construye el mapa actual
                mapa = XML_DeParser.generarMapa(mapaxml, bandos[0], bandos[1]);

                //  Crea el arreglo de los personajes donde comenzara el turno
                personajes = new Personaje[2];

                personajes[0] = bandos[0].getPersonajePrincipal();
                personajes[1] = bandos[1].getPersonajePrincipal();

                turno = turnoJugador;

                bandos[0].setDinero(dinero[0]);
                bandos[1].setDinero(dinero[1]);

                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    /**
     * Setea al batallón que ingresa como parámetro para que sea manejado por
     * el jugador del turno.
     */
    public void manejarBatallon(Personaje personaje) {
        personajes[turno] = personaje;
    }
//    </editor-fold>
}
